//
//  PosPrinter.swift
//  imember_printer
//
//  Created by feibi on 2022/1/24.
//

import UIKit
import PrinterSDK

/// 紙張尺寸
enum POSPaperSize {
    case mm58
    case mm80
}

class PosPrinter: NSObject {
    /// ESC指令
    private let esc: PTCommandESC
    
    /// 紙張尺寸
    private let pagerSize: POSPaperSize
    
    /// 纸张宽度
    private var paperWidth: Int {
        switch pagerSize {
        case .mm58: return 384
        case .mm80: return 576
        }
    }
    
    /// 编码格式
    private let encoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.big5.rawValue))
    
    /// POS打印機初始化
    ///
    /// paperSize：紙張尺寸，只支持58mm和80mm，默認為80mm
    init(paperSize: POSPaperSize = .mm80) {
        pagerSize = paperSize
        
        esc = PTCommandESC()
        esc.initializePrinter()
        esc.encoding = encoding
    }
    
    // MARK: - Public_M
    public func appendRow(columns: [TextColumn]) {
        let isSumValid = columns.reduce(0) { $0 + $1.flex } == 12
        if !isSumValid {
            assertionFailure("Total columns width must be equal to 12")
            return
        }
        
        var columnSplitTextList = [[String]]()
        for i in 0 ..< columns.count {
            let column = columns[i]
            let columnWitdh = paperWidth / 12 * column.flex
            columnSplitTextList.append(splitColumnText(column.text, columnWidth: columnWitdh))
        }
        let rowTextList = getRowTextList(columns: columns, columnSplitTextList: columnSplitTextList)
        rowTextList.forEach() {
            esc.setJustification(0)
            esc.appendText($0)
        }
    }
    
    public func appendText(_ text: String, justification: Int = 0) {
        esc.setJustification(justification)
        esc.appendText(text)
    }
    
    public func appendDateTime() {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy/MM/dd HH:mm"
        let dateText = formatter.string(from: Date())
        
        esc.setJustification(1)
        esc.appendText(dateText)
    }
    
    public func appendDivide() {
        let num = paperWidth / charWidth("-")
        let divideText = stride(from: 0, to: num, by: 1).map {_ in "-" }.joined()
        
        esc.setJustification(0)
        esc.appendText(divideText)
    }
    
    public func appendLines(_ lines: Int) {
        esc.printAndFeedLines(lines)
    }
    
    public func appendTitle(_ title: String) {
        appendLines(1)
        esc.setReverse(1)
        esc.setCharacterSize(1)
        appendRow(columns: [TextColumn(text: title, flex: 12, justification: 1)])
        esc.setReverse(0)
        esc.setCharacterSize(0)
        appendLines(1)
    }
    
    public func appendQRCode(_ data: String) {
        esc.appendQRCodeData(data, justification: 1, leftMargin: 0, eccLevel: 48, model: 49, size: 8)
    }
    
    public func appendImage(_ imageData: Data) {
        let image = UIImage(data: imageData)?.cgImage
        if let image = image {
            esc.appendRasterImage(image, mode: .dithering, compress: .LZO, package: false)
        }
    }
    
    public func kickCashDrawer(number: Int) {
        esc.kickCashdrawer(number)
    }
    
    public func sendData(result: @escaping FlutterResult) {
        if PTDispatcher.share().printerConnected == nil {
            ProgressHUDManager.showInfo(message: "打印機未連接，請先連接")
            result(FlutterError.customizeFailedMessage("The printer is not connected, please connect first"))
            return
        }
        
        esc.setFullCutWithDistance(100)
        PTDispatcher.share().send(esc.getCommandData())
        
        PTDispatcher.share().whenSendSuccess { _, _ in
            ProgressHUDManager.showSuccess(message: "數據發送成功")
            result(nil)
        }
        
        PTDispatcher.share().whenSendFailure {
            ProgressHUDManager.showSuccess(message: "數據發送失敗")
            result(FlutterError.customizeFailedMessage("Failed to send data"))
        }
    }
    
    // MARK: - Private_M
    private func getRowTextList(columns: [TextColumn], columnSplitTextList: [[String]]) -> [String] {
        /// 计算总共行数
        var totoalRow = 0
        columnSplitTextList.forEach() { totoalRow = max(totoalRow, $0.count) }
        /// 每行的文字
        var rowTextList = [String]()
        /// 循环打印每行的数据
        for i in 0 ..< totoalRow {
            var rowText = ""
            /// 循环打印每列的数据
            for j in 0 ..< columns.count {
                let columnSplitText = columnSplitTextList[j][safe: i] // 需要判断是否为空
                let columnWitdh = paperWidth / 12 * columns[j].flex
                let columnText = getColumnText(columnSplitText, columnWidth : columnWitdh, columnJustification: columns[j].justification)
                rowText.append(columnText)
            }
            rowTextList.append(rowText)
        }
        return rowTextList
    }
    
    private func getColumnText(_ text: String?, columnWidth: Int, columnJustification: Int) -> String {
        guard var text = text else {
            let spaceNum = columnWidth / charWidth(" ")
            let spaceText = stride(from: 0, to: spaceNum, by: 1).map {_ in " "}.joined()
            return spaceText
        }
        
        let textWidth = text.reduce(0) { $0 + charWidth($1) }
        
        if textWidth < columnWidth {
            let spaceNum = (columnWidth - textWidth) / charWidth(" ")
            if columnJustification == 0 {               /// 居左
                return text + String(repeating: " ", count: spaceNum)
            } else if columnJustification == 1 {    /// 居中
                return String(repeating: " ", count: spaceNum / 2) + text + String(repeating: " ", count: spaceNum / 2 + (spaceNum % 2 == 0 ? 0 : 1))
            } else {        /// 居右
                return String(repeating: " ", count: spaceNum) + text
            }
        } else if textWidth == columnWidth {
            return text
        } else  {
            return getColumnText(String(text.removeLast()), columnWidth: columnWidth, columnJustification: columnJustification)
        }
    }
    
    /// 将每列的文字分割成字符串数组
    func splitColumnText(_ text: String?, columnWidth: Int) -> [String] {
        guard let text = text else {
            return [String]()
        }
        
        var start = 0
        var splitText = ""
        var splitTextList = [String]()
        for (index, char) in text.enumerated() {
            /// 每个字符的宽度
            let charWidth = charWidth(char)
            /// 需判断是否超过了总宽度，若超过需要另起一行打印
            if start + charWidth < columnWidth {
                splitText.append(char)
                start += charWidth
                if index == text.count - 1 {
                    splitTextList.append(splitText)
                }
            } else if start + charWidth == columnWidth {
                splitText.append(char)
                splitTextList.append(splitText)
                start = 0
                splitText = ""
            } else {
                splitTextList.append(splitText)
                start = charWidth
                splitText = String(char)
                if index == text.count - 1 {
                    splitText.append(splitText)
                }
            }
        }
        return splitTextList
    }
    
    /// 字符占据的宽度(中文：24，英文：12)
    private func charWidth(_ char: Character) -> Int {
        return char.isASCII ? 12 : 24
    }
}

fileprivate extension Array {
    subscript(safe index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

